﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CarbonExchange.AmeeQuery;
using CarbonExchange.Dal;

namespace CarbonExchange.Bll
{
    public class CommitmentFactory : FactoryBase<Commitment>
    {
        public CommitmentFactory() : base() { }

        public CommitmentFactory(IAmeeCommand ameeCommand) : base(ameeCommand) { }

        public CommitmentFactory(Context dalContext) : base(dalContext) { }

        public CommitmentFactory(IAmeeCommand ameeCommand, Context dalContext) : base(ameeCommand, dalContext) { }

        protected override void MarkItemForDeletion(Commitment item)
        {
            var dataItem = item.Convert(DalContext);
            DalContext.Commitments.DeleteOnSubmit(dataItem);
        }

        protected override void Validate(IQueryable<Commitment> items, CarbonExchange.Bll.Exceptions.ValidationException exp)
        {
            items.Validate(exp, DalContext);
        }

        protected override void PopulateDataChanges(Commitment item, object dataItem)
        {
            var tmp = dataItem as Dal.Commitment;
            if (tmp != null)
            {
                if (item.Id == 0)
                {
                    item.Id = tmp.Id;
                    item.PledgeName = tmp.Pledge.Name;
                    item.PledgeText = tmp.Pledge.Text;
                    item.PledgeCategoryId = tmp.Pledge.PledgeCategoryId;
                    item.PledgeCategoryName = tmp.Pledge.PledgeCategory.Name;
                    item.SectionId = tmp.Pledge.PledgeCategory.SectionId;
                    item.SectionName = tmp.Pledge.PledgeCategory.Section.Name;
                    item.AveSavingPerMonth = tmp.Pledge.AveSavingPerMonth;
                }
                item.CID = tmp.CID;
            }
        }

        protected override object GetDataItemForUpdate(Commitment item)
        {
            return item.Convert(DalContext);
        }

        internal class JoinHelper
        {
            internal Dal.Commitment Commitment { get; set; }
            internal Dal.Pledge Pledge { get; set; }
            internal Dal.PledgeCategory PledgeCategory { get; set; }
            internal Dal.Section Section { get; set; }
        }

        private IQueryable<JoinHelper> GetSelection()
        {
            return base.DalContext.Commitments.Where(x => !x.Pledge.Deleted && !x.Pledge.PledgeCategory.Deleted && !x.Pledge.PledgeCategory.Section.Deleted).OrderBy(x => x.CommitmentDate).Select(x => new JoinHelper() { Commitment = x, Pledge = x.Pledge, PledgeCategory = x.Pledge.PledgeCategory, Section = x.Pledge.PledgeCategory.Section });
        }

        /// <summary>
        /// Returns all commitments by a user
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public IQueryable<Commitment> GetAllByUserId(int userId)
        {
            return GetSelection().Where(x => x.Commitment.UserId == userId).Convert();
        }

        /// <summary>
        /// Returns All Commitments that havnt had the commitment date set yet
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public IQueryable<Commitment> GetAllPendingByUserId(int userId)
        {
            return GetSelection().Where(x => x.Commitment.UserId == userId && x.Commitment.CommitmentDate == null).Convert();
        }

        /// <summary>
        /// Gets All Commitemnts with in a commitement date
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="commitemntDate"></param>
        /// <returns></returns>
        public IQueryable<Commitment> GetAllByUserIdAndCommitmentDate(int userId, DateTime commitemntDate)
        {
            return GetSelection().Where(x => x.Commitment.UserId == userId && x.Commitment.CommitmentDate != null && x.Commitment.CommitmentDate.Value.Date == commitemntDate.Date).Convert();
        }

        /// <summary>
        /// All the Seperate Commitement Dates
        /// </summary>
        /// <param name="userID"></param>
        /// <returns></returns>
        public DateTime[] GetCommitmentDatesByUserId(int userID)
        {
            return GetSelection().Where(x => x.Commitment.UserId == userID && x.Commitment.CommitmentDate != null).Select(x => x.Commitment.CommitmentDate.Value.Date).Distinct().OrderByDescending(x => x).ToArray();
        }


        public DateTime CreateCommitments(List<Commitment> pending)
        {
            if(pending.Where(x => x.CommitmentDate != null).Count() > 0)
                throw new ApplicationException("List of pending commitments contains a commitment with a CommmitementDate already set");
            var date = DateTime.Now;
            foreach (var item in pending)
            {
                item.CommitmentDate = date;
            }
            base.Save(pending);
            return date;
        }

        public int GetTotalCarbonPledged()
        {
            return Convert.ToInt32(DalContext.Commitments.Where(x => !x.Pledge.Deleted).Sum(commitement => commitement.Pledge.AveSavingPerMonth)) * 12;
        }
    }

    internal static class CommitmentConverter
    {
        internal static IQueryable<Commitment> Convert(this IQueryable<CommitmentFactory.JoinHelper> source)
        {
            return source.Select(x => x.Convert());
        }

        internal static Commitment Convert(this CommitmentFactory.JoinHelper source)
        {
            if (source != null)
            {
                return new Commitment()
                {
                    Id = source.Commitment.Id,
                    CID = source.Commitment.CID,
                    CommitmentDate = source.Commitment.CommitmentDate.ToServerTime(),
                    Completed = source.Commitment.Completed,
                    PledgeId = source.Commitment.PledgeId,
                    UserId = source.Commitment.UserId,
                    PledgeName = source.Pledge.Name,
                    PledgeText = source.Pledge.Text,
                    PledgeCategoryId = source.Pledge.PledgeCategoryId,
                    PledgeCategoryName = source.Pledge.PledgeCategory.Name,
                    SectionId = source.Pledge.PledgeCategory.SectionId,
                    SectionName = source.Pledge.PledgeCategory.Section.Name,
                    AveSavingPerMonth = source.Pledge.AveSavingPerMonth
                };
            }
            else
            {
                return null;
            }
        }

        internal static IQueryable<Dal.Commitment> Convert(this IQueryable<Commitment> source, Dal.Context context)
        {
            return source.Select(x => x.Convert(context));
        }

        internal static Dal.Commitment Convert(this Commitment source, Dal.Context context)
        {
            Dal.Commitment dataItem = null;
            if (source.Id > 0)
            {
                dataItem = context.Commitments.Where(x => x.Id == source.Id).Single();
            }
            else
            {
                dataItem = new CarbonExchange.Dal.Commitment();
                context.Commitments.InsertOnSubmit(dataItem);
            }
            dataItem.UserId = source.UserId;
            dataItem.PledgeId = source.PledgeId;
            dataItem.CommitmentDate = source.CommitmentDate.ToDBTime();
            dataItem.Completed = source.Completed;

            return dataItem;
        }

        internal static void Validate(this IQueryable<Commitment> source, Exceptions.ValidationException e, Dal.Context context)
        {
            foreach (var item in source)
            {
                item.Validate(e, context);
            }
        }

        internal static void Validate(this Commitment source, Exceptions.ValidationException e, Dal.Context context)
        {
        }
    }
}
